/**********************************************************

@description:
*File Name:cpu.c
*Author: ydx
*mail: yudongxiang93@163.com
*Create Time: Wed 04 Jun 2025 05:47:30 PM CST
**********************************************************/
#include "cpu.h"
#include "os_cfg.h"
#include "cpu_instr.h"
#include "irq.h"
#include "mutex.h"
#include "syscall.h"

static segment_desc_t gdt_table[GDT_TABLE_SIZE] __attribute__((section(".data")));
static mutex_t        mutex;

void segment_desc_set(int selector, uint32_t base, uint32_t limit, uint16_t attr)
{
    // 如果limit大于0xFFFFF,则设置G位,limit除以0x1000,Granularity=1,segment limit=4KB,
    if (limit > 0xFFFFF)
    {
        attr |= 0x8000;
        limit /= 0x1000;    // 20+12=32位,limit=4KB
    }
    segment_desc_t *desc = (segment_desc_t *)(gdt_table + selector / sizeof(segment_desc_t));
    desc->limit_low      = limit & 0xFFFF;
    desc->base_low       = base & 0xFFFF;
    desc->base_mid       = (base >> 16) & 0xFF;
    desc->attr           = attr | (((limit >> 16) & 0x0F) << 8);
    desc->base_high      = (base >> 24) & 0xFF;
}
void gdt_init()
{
    for (int i = 0; i < GDT_TABLE_SIZE; i++)
    {
        segment_desc_set(i * sizeof(segment_desc_t), 0, 0, 0);
    }
    segment_desc_set(KERNEL_SELECTOR_DS,
                     0,
                     0xFFFFFFFF,
                     SEG_P | SEG_DPL0 | SEG_S_CODE_DATA | SEG_TYPE_DATA | SEG_TYPE_DATA_WRITE | SEG_D);
    segment_desc_set(KERNEL_SELECTOR_CS,
                     0,
                     0xFFFFFFFF,
                     SEG_P | SEG_DPL0 | SEG_S_CODE_DATA | SEG_TYPE_CODE | SEG_TYPE_CODE_READ | SEG_D);

    gate_desc_set((gate_desc_t *)(gdt_table + (SELECTOR_SYSCALL >> 3)),
                  KERNEL_SELECTOR_CS,
                  (uint32_t)exception_handler_syscall,
                  SYSCALL_GATE_P | SYSCALL_GATE_DPL3 | SYSCALL_GATE_TYPE32 | SYSCALL_GATE_PARAM_COUNT);
    // lgdt((uint32_t)gdt_table,GDT_TABLE_SIZE*sizeof(segment_desc_t));
    lgdt((uint32_t)gdt_table, sizeof(gdt_table));
}
int gdt_alloc_desc()
{
    // irq_state_t irq_state = irq_enter_protect();
    mutex_lock(&mutex);
    for (int i = 1; i < GDT_TABLE_SIZE; i++)
    {
        segment_desc_t *desc = gdt_table + i;
        if (desc->attr == 0)
        {
            // irq_exit_protect(irq_state);
            mutex_unlock(&mutex);
            return i * sizeof(segment_desc_t);
        }
    }
    // irq_exit_protect(irq_state);
    mutex_unlock(&mutex);
    return -1;
}

void switch_to_tss(int tss_selector)
{
    far_jump(tss_selector, 0);
}
void cpu_init()
{
    mutex_init(&mutex);
    gdt_init();
}
void gdt_free_selector(int selector)
{
    mutex_lock(&mutex);
    gdt_table[selector / sizeof(segment_desc_t)].attr = 0;
    mutex_unlock(&mutex);
}
